package restapi

import (
	"encoding/json"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
	sqlModels "ubi-helper/src/db/sql"
)

// 查询节点静态信息
func findNodeStatics() error {
	var nodes []sqlModels.NodeInfo
	result := ubiScaleViewDB.Table("node_info").Find(&nodes)
	// 数据库异常的情况(非查询结果0件的情况)直接返回
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return result.Error
		logError("findNodeStatics error", result.Error)
	}
	logInfo("findNodeStatics", nodes)
	return nil
}

// 获取节点静态信息
func getNodeStatics(ipAddr string) (*SysInfo, error) {
	query := createUrl("http:", "//", ipAddr, NodeExporterPort, "/statics")
	client := http.Client{}
	client.Timeout = 5 * time.Second
	u, err := url.Parse(query)
	resp, err := client.Get(u.String())
	if err != nil {
		logError("get node statics from api error", ipAddr, err)
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logError("get node statics from api error", ipAddr, err)
		return nil, err
	}

	sysInfo := &SysInfo{}
	err = json.Unmarshal(body, sysInfo)
	if err != nil {
		logError("get node statics from api error", ipAddr, err)
		return nil, err
	}

	sysInfo.PrimaryIp = ipAddr
	return sysInfo, nil
}

// 新增节点静态信息
func createNodeStatics(si *SysInfo) error {
	host := si.Host
	var cpu CpuInfoStat
	if len(si.CPU) > 0 {
		cpu = si.CPU[0]
	}
	memory := si.Memory
	// 硬盘需要过滤
	disks := si.Disk
	//for i := 0; i < len(disks); {
	//	text := disks[i].Path
	//	// 硬盘path进行正则过滤
	//	re := regexp.MustCompile("/dev/sd[a-zA-Z0-9]+")
	//	match := re.FindString(text)
	//	if match == "" {
	//		disks = append(disks[:i], disks[i+1:]...)
	//	} else {
	//		i++
	//	}
	//}
	// 端口
	interfaces := si.Interface
	// 管理ip
	primaryIp := si.PrimaryIp
	// 在事务中执行一些 db 操作
	error := ubiScaleViewDB.Transaction(func(tx *gorm.DB) error {
		node := sqlModels.NodeInfo{
			NodeName:           host.Hostname,
			NodeIpv4:           si.PrimaryIp,
			NodeIpv6:           "",
			Manufacturer:       "", // 服务器制造商 无
			Model:              "", // 服务器型号 无
			CpuModel:           cpu.ModelName,
			CpuArchitecture:    host.KernelArch,
			CpuClockRate:       cpu.Mhz,
			RamCapacity:        int64(memory.Total),
			RamManufacturer:    "", // 内存制造商 无
			RamFrequency:       0,  // 内存频率 无
			HdiskNum:           int8(len(disks)),
			HdiskTotalCapacity: 0,  // TODO 待完善
			NodePosition:       "", // 无
			OperationSystem:    host.OS + "-" + host.Platform,
			OsVersion:          host.PlatformVersion,
			ClusterId:          0,
			Audit: sqlModels.Audit{
				Creator:        "system",
				CreateTime:     time.Now(),
				Updater:        "system",
				LastUpdateTime: time.Now(),
			},
		}
		// 根据管理ip查询节点信息
		var nodeDb sqlModels.NodeInfo
		err := tx.Table("node_info").Where(&sqlModels.NodeInfo{NodeIpv4: primaryIp}).First(&nodeDb).Error
		// 数据库不存在，新增
		if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
			// 新增节点数据
			if err := tx.Table("node_info").Create(&node).Error; err != nil {
				return err
			}
			logInfo("新增节点数据成功", node.NodeId)
		} else if err == nil {
			// 更新节点数据
			if err := tx.Table("node_info").Omit("CreateTime", "Creator").Model(&nodeDb).Updates(&node).Error; err != nil {
				return err
			}
			node.NodeId = nodeDb.NodeId
			// 删除关联的硬盘数据、端口数据
			tx.Table("hdisk_info").Where("node_id=?", nodeDb.NodeId).Delete(sqlModels.DiskInfo{})
			tx.Table("port_info").Where("node_id=?", nodeDb.NodeId).Delete(sqlModels.PortInfo{})
			logInfo("更新节点数据成功", node.NodeId)
		}
		// 新增硬盘数据
		diskInfos := make([]sqlModels.DiskInfo, 0, 32)
		var sumCapacity int64
		for i, v := range disks {
			diskInfo := sqlModels.DiskInfo{
				HdiskName:     "硬盘" + strconv.Itoa(i+1),
				HdiskCapacity: int64(v.Total),
				HdiskMountDir: v.Path,
				HdiskFS:       v.Fstype,
				NodeID:        node.NodeId,
				Audit: sqlModels.Audit{
					Creator:        "system",
					CreateTime:     time.Now(),
					Updater:        "system",
					LastUpdateTime: time.Now(),
				},
			}
			sumCapacity += int64(v.Total)
			diskInfos = append(diskInfos, diskInfo)
		}
		if err := tx.Table("hdisk_info").Omit("NodeName", "NodeIpv4").CreateInBatches(diskInfos, 64).Error; err != nil {
			return err
		}
		// 新增端口数据
		portInfos := make([]sqlModels.PortInfo, 0, 32)
		for _, v := range interfaces {
			portInfo := sqlModels.PortInfo{
				PortName:     v.Name,
				PortType:     0,
				ProtocolType: "",
				MacAddress:   v.HardwareAddr,
				Bandwidth:    int64(v.MTU),
				NodeID:       node.NodeId,
				Audit: sqlModels.Audit{
					Creator:        "system",
					CreateTime:     time.Now(),
					Updater:        "system",
					LastUpdateTime: time.Now(),
				},
			}
			for _, addr := range v.Addrs {
				if isIPv4(addr.Addr) {
					portInfo.PortIPv4 = addr.Addr
				} else {
					portInfo.PortIPv6 = addr.Addr
				}
			}
			portInfos = append(portInfos, portInfo)
		}
		if err := tx.Table("port_info").Omit("NodeName", "NodeIpv4").CreateInBatches(portInfos, 64).Error; err != nil {
			return err
		}
		// 节点数据加入硬盘总容量
		node.HdiskTotalCapacity = sumCapacity

		return nil
	})

	if error != nil {
		logError("写入节点静态数据到数据库失败", error)
	} else {
		logError("写入节点静态数据到数据库成功")
	}

	return nil
}

func isIPv4(address string) bool {
	return strings.Count(address, ":") < 2
}

func isIPv6(address string) bool {
	return strings.Count(address, ":") >= 2
}
